/**
 * (C) Copyright IBM Corp. 2010, 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.ibm.bi.dml.parser;

import java.util.HashMap;

import com.ibm.bi.dml.lops.Lop;


public class IterablePredicate extends Expression 
{
	
	private DataIdentifier _iterVar;	// variable being iterated over
	private Expression _fromExpr;
	private Expression _toExpr;
	private Expression _incrementExpr;
	private HashMap<String,String> _parforParams;
	
	
	public IterablePredicate(DataIdentifier iterVar, Expression fromExpr, Expression toExpr, 
			Expression incrementExpr, HashMap<String,String> parForParamValues,
			String filename, int blp, int bcp, int elp, int ecp)
	{
		_iterVar = iterVar;
		_fromExpr = fromExpr;
		_toExpr = toExpr;
		_incrementExpr = incrementExpr;
		
		_parforParams = parForParamValues;
		this.setAllPositions(filename, blp, bcp, elp, ecp);
	}
		
	public String toString()
	{ 
		StringBuilder sb = new StringBuilder();
		sb.append( "(" );
		sb.append( _iterVar.getName() );
		sb.append(" in seq(");
		sb.append(_fromExpr.toString());
		sb.append(",");
		sb.append(_toExpr.toString());
		sb.append(",");
		sb.append(_incrementExpr.toString());
		sb.append( ")" );
		if (_parforParams != null && _parforParams.size() > 0){
			for (String key : _parforParams.keySet())
			{
				sb.append( "," );
				sb.append( key );
				sb.append( "=" );
				sb.append( _parforParams.get(key) );
			}
		}
		sb.append( ")" );
		return sb.toString();
	}
	
	 
	public VariableSet variablesRead() 
	{
		VariableSet result = new VariableSet();
		result.addVariables( _fromExpr.variablesRead()      );
		result.addVariables( _toExpr.variablesRead()        );
		result.addVariables( _incrementExpr.variablesRead() );

		return result;
	}

	 
	public VariableSet variablesUpdated() 
	{
		VariableSet result = new VariableSet();
		result.addVariable(_iterVar.getName(), _iterVar);
		
	 	return result;
	}

	@Override
	public Expression rewriteExpression(String prefix) throws LanguageException {
		//DataIdentifier newIterVar = (DataIdentifier)_iterVar.rewriteExpression(prefix);
		//return new IterablePredicate(newIterVar, _from, _to, _increment);
		LOG.error(this.printErrorLocation() + "rewriteExpression not supported for IterablePredicate");
		throw new LanguageException(this.printErrorLocation() + "rewriteExpression not supported for IterablePredicate");
		
	}

	@Override
	public void validateExpression(HashMap<String, DataIdentifier> ids, HashMap<String, ConstIdentifier> constVars, boolean conditional) 
		throws LanguageException 
	{		
		
		//recursive validate
		if (_iterVar instanceof FunctionCallIdentifier
				|| _fromExpr instanceof FunctionCallIdentifier
				||	_toExpr instanceof FunctionCallIdentifier
				||	_incrementExpr instanceof FunctionCallIdentifier){
			raiseValidateError("user-defined function calls not supported for iterable predicates", 
		            false, LanguageException.LanguageErrorCodes.UNSUPPORTED_EXPRESSION);
		}
		
		
		
		//1) VALIDATE ITERATION VARIABLE (index)
		// check the variable has either 1) not been defined already OR 2) defined as integer scalar   
		if (ids.containsKey(_iterVar.getName())){
			DataIdentifier otherDI = ids.get(_iterVar.getName());
			if( otherDI.getDataType() != DataType.SCALAR || otherDI.getValueType() != ValueType.INT ){
				raiseValidateError("iterable predicate in for loop '" + _iterVar.getName() + "' must be a scalar integer", conditional);
			}	
		}
		
		// set the values for DataIdentifer iterable variable
		_iterVar.setIntProperties();
			
		// add the iterVar to the variable set
		ids.put(_iterVar.getName(), _iterVar);
		
		
		//2) VALIDATE FOR PREDICATE in (from, to, increment)		
		//recursively validate the individual expression
		_fromExpr.validateExpression(ids, constVars, conditional);
		_toExpr.validateExpression(ids, constVars, conditional);
		_incrementExpr.validateExpression(ids, constVars, conditional);
		
		//check for scalar expression output
		checkNumericScalarOutput( _fromExpr );
		checkNumericScalarOutput( _toExpr );
		checkNumericScalarOutput( _incrementExpr );
	}
		
	public DataIdentifier getIterVar() {
		return _iterVar;
	}

	public void setIterVar(DataIdentifier iterVar) {
		_iterVar = iterVar;
	}

	public Expression getFromExpr() {
		return _fromExpr;
	}

	public void setFromExpr(Expression from) {
		_fromExpr = from;
	}

	public Expression getToExpr() {
		return _toExpr;
	}

	public void setToExpr(Expression to) {
		_toExpr = to;
	}

	public Expression getIncrementExpr() {
		return _incrementExpr;
	}

	public void setIncrementExpr(Expression increment) {
		_incrementExpr = increment;
	}
	
	public HashMap<String,String> getParForParams(){
		return _parforParams;
	}
	
	public void setParForParams(HashMap<String,String> params){
		_parforParams = params;
	}
	
	public static String[] createIterablePredicateVariables( String varName, Lop from, Lop to, Lop incr )
	{
		String[] ret = new String[4]; //varname, from, to, incr
		
		ret[0] = varName;
		
		if( from.getType()==Lop.Type.Data )
			ret[1] = from.getOutputParameters().getLabel();
		if( to.getType()==Lop.Type.Data )
			ret[2] = to.getOutputParameters().getLabel();
		if( incr.getType()==Lop.Type.Data )
			ret[3] = incr.getOutputParameters().getLabel();
		
		return ret;
	}

	private void checkNumericScalarOutput( Expression expr )
		throws LanguageException
	{
		if( expr == null || expr.getOutput() == null )
			return;
		
		Identifier ident = expr.getOutput();
		if( ident.getDataType() == DataType.MATRIX || ident.getDataType() == DataType.OBJECT ||
			(ident.getDataType() == DataType.SCALAR && (ident.getValueType() == ValueType.BOOLEAN || 
					                                    ident.getValueType() == ValueType.STRING || 
					                                    ident.getValueType() == ValueType.OBJECT)) )
		{
			LOG.error(this.printErrorLocation() + "expression in iterable predicate in for loop '" + expr.toString() + "' must return a numeric scalar");
			throw new LanguageException(this.printErrorLocation() + "expression in iterable predicate in for loop '" + expr.toString() + "' must return a numeric scalar");
		}
	}

} // end class
